package debtbundler.presentation.basecomponents;

import java.awt.Component;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import javax.swing.JComponent;

/**
 * A component class that can be marked as selected.
 * It also notifies its <code>MouseListener</code>s and <code>FocusListener</code>s 
 * of all of its contained components' mouse and focus events.
 *  
 * @author Anssi
 *
 */
public abstract class SelectableComponent extends JComponent implements MouseListener, FocusListener {
	private static final long serialVersionUID = 1L;
	
	protected boolean isSelected = false;
	
	public SelectableComponent() {
		super();
		addMouseListener(this);
	}

	public boolean isSelected() {
		return isSelected;
	}

	public void setSelected(boolean isSelected) {
		this.isSelected = isSelected;
	}

	@Override
	protected void addImpl(Component comp, Object constraints, int index) 
				throws IllegalArgumentException, NullPointerException {
		
		super.addImpl(comp, constraints, index);
		listenTo(comp);
	}
	
	@Override
	public void remove(Component comp) {
		stopListeningTo(comp);
	}
	
	@Override
	public void mouseClicked(MouseEvent e) {
		MouseEvent wrapper = wrapEvent(e);
		for (MouseListener ml : this.getMouseListeners()) {
			if (ml != this) {
				ml.mouseClicked(wrapper);
			}
		}
	}

	@Override
	public void mouseEntered(MouseEvent e) {
		MouseEvent wrapper = wrapEvent(e);
		for (MouseListener ml : this.getMouseListeners()) {
			if (ml != this) {
				ml.mouseEntered(wrapper);
			}
		}
	}

	@Override
	public void mouseExited(MouseEvent e) {
		MouseEvent wrapper = wrapEvent(e);
		for (MouseListener ml : this.getMouseListeners()) {
			if (ml != this) {
				ml.mouseExited(wrapper);
			}
		}	
	}

	@Override
	public void mousePressed(MouseEvent e) {
		MouseEvent wrapper = wrapEvent(e);
		for (MouseListener ml : this.getMouseListeners()) {
			if (ml != this) {
				ml.mousePressed(wrapper);
			}
		}
	}

	@Override
	public void mouseReleased(MouseEvent e) {
		MouseEvent wrapper = wrapEvent(e);
		for (MouseListener ml : this.getMouseListeners()) {
			if (ml != this) {
				ml.mouseReleased(wrapper);
			}
		}
	}

	@Override
	public void focusGained(FocusEvent e) {
		FocusEvent wrapper = wrapEvent(e);
		for (FocusListener fl : this.getFocusListeners()) {
			if (fl != this) {
				fl.focusGained(wrapper);
			}
		}
	}

	@Override
	public void focusLost(FocusEvent e) {
		FocusEvent wrapper = wrapEvent(e);
		for (FocusListener fl : this.getFocusListeners()) {
			if (fl != this) {
				fl.focusLost(wrapper);
			}
		}
	}
	
	private void listenTo(Component comp) {
		comp.addMouseListener(this);
		comp.addFocusListener(this);
		
		if (comp instanceof JComponent) {
			JComponent jc = (JComponent) comp;
			for (Component child : jc.getComponents()) {
				listenTo(child);
			}
		}
	}
	
	private void stopListeningTo(Component comp) {
		comp.removeMouseListener(this);
		comp.removeFocusListener(this);
		
		if (comp instanceof JComponent) {
			JComponent jc = (JComponent) comp;
			for (Component child : jc.getComponents()) {
				stopListeningTo(child);
			}
		}
	}
	
	private MouseEvent wrapEvent(MouseEvent e) {
		MouseEvent wrapper = new MouseEvent(
									this, 
									e.getID(), 
									e.getWhen(), 
									e.getModifiers(),
									e.getX(), 
									e.getY(),
									e.getClickCount(),
									e.isPopupTrigger(),
									e.getButton());
		return wrapper;
	}
	
	private FocusEvent wrapEvent(FocusEvent e) {
		FocusEvent wrapper = new FocusEvent(
									this, 
									e.getID(),
									e.isTemporary(),
									e.getOppositeComponent());
		return wrapper;
	}	
}
